<?php

namespace Service\CommodityService;

use Service\BaseService;
use Service\Code\Normal;
use Hyperf\DbConnection\Db;
use App\Model\CommodityModel\CommodityOptimizationBbc;
use App\Model\CommodityModel\SupplierCommodityTag;
use App\Model\PssModel\TemplateDelivery;
use App\Model\CommodityModel\CommodityChannelBestGoods;
use App\Model\PssModel\Business;
use Service\AppService\SystemCacheConfigService;
use App\Model\CommodityModel\CommodityArchives;
use Hyperf\Utils\WaitGroup;
use App\Model\PssModel\MerchantGoodsSyncLog;
use App\Model\CommodityModel\SupplierCommodity;
use App\Model\CommodityModel\CommoditySpecValue;

/**
 * 渠道商品服务中心
 * @author chenshubo
 */
class BbcBestGoodsService extends BaseService
{
    protected $alisa_table;
    //百分率基数
    const BASE_PRICE_RATE       = 1.00;
    //小数点位数
    const BASE_SCALE_TYPE       = 4;
    //金额计算精度
    const BASE_FLOAT            = 3;
    
    /**
     * 获取实际下单调度的商品
     * 获取原始商品价格信息
     * @param int $bid
     * @param array $SkuIDs
     * @param int $is_router_user
     */
    public function getDispatchOrderGoodsList( $bid = 0, $SkuIDs = [],$is_router_user = 0)
    {
        $where = [];
        //正常优选逻辑
        if( $is_router_user == 0 ) {
            $string = implode("','",$SkuIDs);
            $_where[] = "cop.BuID = {$bid} AND cop.IsInit = 1 AND cop.SkuID IN ('{$string}')";
        } else {
            //路由供应商商品渠道逻辑
            $string = implode("','", $SkuIDs);
            $_where[] = "cop.BuID = {$bid} AND cop.IsInit = 1 AND cop.SupplierSKUCode IN ('{$string}')";
        }
        
        //处理取路由模式下单字段
        $_fields_skucode = $is_router_user == 1 ? "SupplierSKUCode" : "SKUCode";
        
        $_where[] = "stock.isDeleted = 0";
        //理由模式渠道用户，不检验云仓库存
        if( $is_router_user != 1 ) {
            $_where[] = " IF ( s.FastDeliveryType = 1, stock.Number >= 0,stock.Number > 0  )";
        }
        
        $_where[] = "stock.IsHasPrice =1";
        $_where[] = "scb.isDeleted = 0";
        $_where[] = "scb.SaleType = 1";
        $_where[] = "scb.UpstreamSaleType = 1";
        $_where[] = "cop.CommodityStatus  = 1";
        
        $table  = CommodityOptimizationBbc::instance()->getTableByBuID($bid)->getTable();
        $list   = CommodityOptimizationBbc::instance()->getTableByBuID($bid)
        ->from($table." as cop")
        ->select([
            Db::raw('csb.ID as SkuID'),
            Db::raw("cop.{$_fields_skucode} AS skuCode"),
            Db::raw("cop.SKUCode"),
            'csb.BarCode',
            'csb.ThirdClassificationID',
            Db::raw('csb.Name as GoodsName'),
            Db::raw("scb.FreightTemplateID AS freightTemplateID"),
            Db::raw("cop.SupplierSKUID AS supplierSKUID"),
            Db::raw('cop.SupplierSKUCode as supplierSKUCode'),
            'cop.IsSaleFirst',
            'cop.IsInit',
            'cop.PriceRuleType',
            'cop.PriceScaleRule',
            'cop.SpecList',
            Db::raw("IFNULL(cds.Images,'') AS Images"),
            Db::raw("u.Name AS UnitName"),
            Db::raw("stock.ID AS stockInfoID"),
            Db::raw("stock.BatchNo AS batchNo"),
            Db::raw("stock.Number AS number"),
            Db::raw("stock.BatchID AS batchID"),
            'stock.CalibrationPeriodTime',
            Db::raw("IFNULL(u_stock.StockRemainNumber,0) AS StockRemainNumber"),
            Db::raw("stock.LimitBuyCount as limitBuyCount"),
            Db::raw('scb.SupplierCommoditySkuCode as supplierCommoditySkuCode'),
            Db::raw('scb.SupplierCommodityType as supplierCommodityType'),
            Db::raw('scb.SupplierCommodityKey as supplierCommodityKey'),
            Db::raw('scb.PriceControlType'),
            Db::raw('scb.LimitOrControlPrice'),
            Db::raw('scb.LimitPriceMode'),
            Db::raw('scb.LimitPriceScale'),
            Db::raw('scb.HSCode as hsCode'),
            Db::raw('csb.MarketPrice'),
            Db::raw('scb.IsHaveTaxRate as isHaveTaxRate'),
            Db::raw('scb.IsHaveTaxRate'),
            Db::raw("IFNULL(scb.TaxRate,0.000) AS taxRate"),
            Db::raw("IFNULL(scb.TaxRate,0.000) AS TaxRate"),
            Db::raw("IFNULL(scb.TaxInComeRate,0.000) AS TaxInComeRate"),
            Db::raw('scb.DeliveryMethod AS deliveryMethod'),
            Db::raw('scb.DefaultStock AS defaultStock'),
            Db::raw('scb.DeclarePlanCode AS declarePlanCode'),
            Db::raw("w.ID AS warehouseID"),
            Db::raw('w.IsCombineOrders AS isCombineOrders'),
            Db::raw('w.WarehouseAccount AS warehouseAccount'),
            Db::raw('w.WarehouseType AS warehouseType'),
            Db::raw('w.WarehouseCode AS warehouseCode'),
            Db::raw('w.WarehouseName AS warehouseName'),
            Db::raw('w.CanModifyStock AS canModifyStock'),
            Db::raw('w.JsonBody'),
            Db::raw("s.ID AS supplierID"),
            Db::raw('s.SupplierCode as supplierCode'),
            Db::raw('s.ThirdPartyPlatformCode as thirdPartyPlatformCode'),
            Db::raw('s.SupplierName as supplierName'),
            Db::raw('s.EnterpriseName as enterpriseName'),
            Db::raw('s.EnterpriseCode as enterpriseCode'),
            Db::raw('s.OrderDeclare as orderDeclare'),
            Db::raw('s.PayDeclare as payDeclare'),
            Db::raw('s.OrderDockMode as orderDockMode'),
            Db::raw('scb_cus.Country as DeclareCountry'),
            Db::raw('scb_cus.CountryCode as DeclareCountryCode'),
            Db::raw('scb_cus.Currency as DeclareCurrency'),
            Db::raw('scb_cus.CurrencyCode as DeclareCurrencyCode'),
            Db::raw('scb_cus.MeasurementUnit as DeclareMeasurementUnit'),
            Db::raw('scb_cus.MeasurementUnitCode as DeclareMeasurementUnitCode'),
            Db::raw('scb_cus.DeclareBarCode as DeclareDeclareBarCode'),
            Db::raw('scb_cus.Function as DeclareFunction'),
            Db::raw('scb_cus.Specifications as DeclareSpecifications'),
            Db::raw('scb_cus.DeclareSKUCode as DeclareDeclareSKUCode'),
            Db::raw('scb_cus.DeclareCommodityName as DeclareDeclareCommodityName'),
            's.IsSettingFreight',
            's.IsSettingFreightTemplate',
            Db::raw('s.FreightTemplate as SupplierFreightTemplate'),
            Db::raw('scb.FreightTemplate as FreightTemplate'),
            Db::raw('csb.Weight as weight'),
            Db::raw('scb.ConSumptionTaxRate as conSumptionTaxRate'),//消费税率
            Db::raw('scb.ConSumptionPrice as conSumptionPrice'),//消费税预警金额
            Db::raw('scb.ConSumptionTaxRate'),//消费税预警金额
            Db::raw('scb.ConSumptionPrice'),//消费税预警金额
            Db::raw('scb.IsCombineSupplierCommodity'),
            Db::raw('scb.CombineGoodsContents'),
            Db::raw('scb.IsInsurance'),
            Db::raw('scb.InsurancePriceDay'),
            Db::raw('s.FastDeliveryType'),
        ])
        ->join("commodityarchives_s2b as csb","csb.ID","=","cop.SkuID")
        ->leftJoin("commoditydescription_s2b as cds",function($join){
            $join->on("cds.CommodityInfoID","=","csb.ID")
            ->where("cds.IsDeleted",0);
        })
        ->join("stockinfo_s2b as stock","stock.ID","=","cop.StockInfoID")
        ->join("suppliercommodity_s2b as scb","scb.ID","=","stock.SupplierCommodityID")
        ->join("warehouseinfo_s2b as w","w.ID","=","stock.WarehouseID")
        ->join("suppliercompany_s2b as s","s.ID","=","w.SupplierID")
        ->leftJoin("classifyinfo_s2b as f_class","f_class.ID","=","csb.FirstClassificationID")
        ->leftJoin("classifyinfo_s2b as s_class","s_class.ID","=","csb.SecondClassificationID")
        ->leftJoin("classifyinfo_s2b as t_class","t_class.ID","=","csb.ThirdClassificationID")
        ->leftJoin("brandinfo_s2b as b","b.ID","=","csb.BrandID")
        ->leftJoin("countryinfo_s2b as c","c.ID","=","csb.CountryID")
        ->leftJoin("unitinfo_s2b as u","u.ID","=","csb.UnitID")
        ->leftJoin("stockinfouser_s2b as u_stock",function($join) use($bid) {
            $join->on("u_stock.StockID","=","stock.ID")
            ->where("u_stock.BuID",$bid)
            ->where("u_stock.IsDisable",0)
            ->where("u_stock.StockRemainNumber",'>',0);
        })
        ->leftJoin("suppliercommoditycustominfo_s2b as scb_cus",function($join){
            $join->on("scb_cus.SupplierCommodityID","=","cop.SupplierSKUID")
            ->where("scb_cus.IsDeleted",0);
        })
        ->whereRaw(implode(" AND ", $_where))
        ->orderBy("cop.IsSaleFirst","desc")//优选售卖
        ->orderBy("cop.BatchSpecMinSinglePrice","asc")//价格最低
        ->get()
        ->toArray();
        $listMap = [];
        
        $SkuIDKey = $is_router_user ? 'skuCode' : 'SkuID';
        foreach ($list as $item ) 
        {
            $specList = @json_decode($item['SpecList'],true);
            $specList = array_map(function( & $value) {
                $value['SpecificationNum'] = (int)$value['SpecificationNum'];
                return $value;
            },$specList);
            
            $item['SpecList'] = $specList;
            $listMap[$item[$SkuIDKey]][$item['IsSaleFirst']][]= $item;
            unset($item);
        }
        return $listMap;
    }
    
    /**
     * skucodes,skuids
     * 获取优选商品数据，全量不带分页
     * 下单调度、授权通知、供应商商品消息变化
     * @param array $param
     */
    public function getDispatchGoodsList( $param = [] )
    {
        $res = $this->getWhereParameters($param);
        if( $res['code'] != Normal::SUC ) {
            return $res;
        }
        
        $where      = $res['data'];
        $bid        = $param['bid'] ? $param['bid'] : 0 ;
        $dataList   = $this->getGoodsQuery($bid,$where)->get()->toArray();
        $dataList   = $this->getDataList($dataList);
        return Normal::returnCode(Normal::SUC,"获取成功",$dataList);
    }
    
    /**
     * 协程处理多渠道用户最新商品授权情况
     * @param array $param
     */
    public function getCopAuthGoodsList( $param = [] )
    {
        $bidsMap = array_column($param,null,'bid');
        $bids = array_keys($bidsMap);
        if( empty($bids) ) {
            return Normal::returnCode(Normal::FAIL,"用户bids组不能为空");
        }
        
        $result = [];
        $count = count($bidsMap);
        $wg = new WaitGroup();
        $wg->add($count);
        foreach ($bidsMap as $item )
        {
            co(function () use ($wg,$item,& $result )
            {
                $bid = (int)$item['bid'];
                $param = [
                    'bid'       => $bid,
                    'IDS'       => $item['IDS'], 
                    'isArchive' => $item['isArchive']
                ];
                $res = $this->getWhereParameters($param);
                if( $res['code'] != Normal::SUC ) {
                    $result[$bid] = [];
                } else {
                    $where = $res['data'];
                    $query = $this->getBestGoodsQuery($bid,$where);
                    $dataList = $this->getLastQuery($bid,$query,$where)->get()->toArray();
                    $dataList = $this->getDataList($dataList);
                    $result[$bid] = $dataList;
                }
                $wg->done();
            });
        }
        $wg->wait();
        return Normal::returnCode(Normal::SUC,"处理成功",$result);
    }
    
    /**
     * 查询优选授权总商品数量
     * @param array $param
     */
    public function getBestGoodsCount( $param = [] )
    {
        $res = $this->getWhereParameters($param);
        if( $res['code'] != Normal::SUC ) {
            return $res;
        }
        
        $where = $res['data'];
        $bid = $param['bid'] ? $param['bid'] : 0 ;
        $count = CommodityChannelBestGoods::instance()->getTableByBuID($bid)->where('BuID',$bid)->where('CommodityStatus',1)->count();
        return Normal::returnCode(Normal::SUC,"获取成功",['count'=>$count]);
    }
    
    /**
     * 组装数据报文
     * @param array $param
     * @param array $dataList
     */
    public function getDataList(array $dataList = [])
    {
        if( empty($dataList) ) {
            return [];
        }
        
        //获取标签数据
        $SupplierCommodityIDs = array_column($dataList,'SupplierCommodityID');
        $tagListMap = SupplierCommodityTag::instance()->getTagNameListBySupplierCommodityIDs($SupplierCommodityIDs);
        //开始处理数据
        foreach ($dataList as & $item) 
        {
            $item['Tags'] = json_encode($tagListMap[$item['SupplierCommodityID']] ?? [],JSON_UNESCAPED_UNICODE);
            $SpecList = @json_decode($item['SpecList'],true);
            $SpecList = array_map(function( $value ) use ($item)
            {
                if(empty($value['SpecNum'])){
                    $value['SpecNum'] = (int)$value['SpecificationNum'];
                }
                if(empty($value['specName'])){
                    $value['specName'] = $value['SpecificationNum'].$item['UnitName'];
                }
                if(empty($value['SpecPrice'])){
                    $value['SpecPrice'] = (float)$value['SpecificationPrice'];
                }
                if(empty($value['SpecFreightPrice'])){
                    $value['SpecFreightPrice'] = (float)($value['SpecificationFreightPrice']??$value['SpecificationPrice']);
                }
                // unset($value['SpecificationNum'],$value['SpecificationPrice']);
                return $value;
            },$SpecList);
            $item['SpecList'] = json_encode($SpecList,JSON_UNESCAPED_UNICODE);
            $item['BatchMap'] = [$item['BatchNo'] => array_column($SpecList,null,'SpecNum')];
            
        }
        
        return $dataList;
    }
    
    /**
     * 获取商品查询条件对象
     * @param int $bid
     * @param array $subQuery
     * @param array $where
     */
    public function getGoodsQuery(int $bid,$where= [],$extra = [])
    {
        $getIsTomoMode = getIsTomoMode();
        $getIsNewBbcJavaMode = getIsNewBbcJavaMode();
        $getIsNeedCrm  = getIsNeedCrm();
        $table = CommodityChannelBestGoods::instance()->getTableByBuID($bid)->getTable();
        $commodity_optimization_table = CommodityOptimizationBbc::instance()->getTableByBuID($bid)->getTable();
        $query = CommodityChannelBestGoods::instance()->getTableByBuID($bid)
        ->from($table." AS a_cop")
        ->select($this->getGoodsColumns())
        ->join("{$commodity_optimization_table} as cop","cop.ID","=","a_cop.CommodityOptimizationID")
        ->join("stockinfo_s2b as stock","stock.ID","=","cop.StockInfoID")
        ->join("commodityarchives_s2b as com",function($join) use ($extra,$getIsTomoMode,$getIsNewBbcJavaMode,$getIsNeedCrm)
        {
            $join->on("com.ID","=","stock.CommodityArchivesID");
            //如果是自营BBC渠道，必须要满足 spu 存在数据
            if( $getIsNewBbcJavaMode == 1 && $getIsNeedCrm == 1 && isset($extra['is_channel']) && $extra['is_channel'] == 1 ) {
                $join->where("com.ThirdPartyCrmLinkStatus",1);
                // ->where("com.SpuID",'>',0)
            }
        })
        ->join("commoditydescription_s2b as cds",function($join)
        {
            $join->on("cds.CommodityInfoID","=","com.ID")
            ->where("cds.IsDeleted",0);
        });
        
        $query
        ->join("suppliercommodity_s2b as scb","scb.ID","=","cop.SupplierSKUID")
        ->leftJoin("suppliercommoditycustominfo_s2b as scb_cus",function($join)
        {
            $join->on("scb_cus.SupplierCommodityID","=","cop.SupplierSKUID")
            ->where("scb_cus.IsDeleted",0);
        })
        ->join("classifyinfo_s2b as f_class","f_class.ID","=","com.FirstClassificationID")
        ->join("classifyinfo_s2b as s_class","s_class.ID","=","com.SecondClassificationID")
        ->join("classifyinfo_s2b as t_class","t_class.ID","=","com.ThirdClassificationID")
        ->join("brandinfo_s2b as b","b.ID","=","com.BrandID")
        ->join("countryinfo_s2b as c","c.ID","=","com.CountryID")
        ->leftJoin("unitinfo_s2b as u","u.ID","=","com.UnitID")
        ->leftJoin("commodity_spu as csp","csp.ID","=","com.SpuID")
        ->join("warehouseinfo_s2b as w","w.ID","=","stock.WarehouseID")
        ->join("suppliercompany_s2b as s","s.ID","=","stock.SupplierCompanyID")
        ->where('a_cop.CommodityStatus',1)
        ->where('a_cop.SelfBuild',0);
        
        //tomo 模式，处理scb 数据必须要 满足备案成功的数据，才可以授权推送
        if( $getIsTomoMode == 1 ) {
            $query->where("scb.LinkEventIdStatus",1)->where("scb.IsLinkGoodsRecord",1);
        }
        
        //如果是有深度分页情况下
        if( !empty($extra['cc_ids']) && is_array($extra['cc_ids'])  ) {
            $query
            ->whereIn('a_cop.ID',$extra['cc_ids']);
        //正常分页
        } else if( isset($extra['page']) && isset($extra['limit']) && !isset($extra['pageLastID'])) {
            $page = ($extra['page'] - 1) * $extra['limit'];
            $query
            ->where('a_cop.BuID',$bid)
            ->whereRaw(implode(" AND ", $where))
            ->offset($page)
            ->limit($extra['limit']);
        //直接通过ID拿数据
        } else if( !empty($extra['page_ids']) ) {
            $query->whereIn('a_cop.ID',$extra['page_ids']);
        } else {
            $query
            ->where('a_cop.BuID',$bid)
            ->whereRaw(implode(" AND ", $where));
        }
        
        return $query;
    }
    
    /**
     * 获取搜索优选和授权参数
     * @param $param
     */
    protected function getWhereParameters( $param = [] )
    {
        $where = [];
        //验证供应商商品类型
        if( !isset($param['bid']) ) {
            return Normal::returnCode(Normal::FAIL,"bid不能为空");
        }

        $bid = $param['bid'] ? $param['bid'] : 0 ;
        $where[] = "a_cop.BuID = {$param['bid']}";
        
        //如果是通过主档案ID搜索
        if( isset($param['isArchive'])  && !empty($param['IDS'])) {
            $string = "'" . implode("','", $param['IDS']) . "'";
            if( $param['isArchive'] ) {
                $where[] = "a_cop.SkuID in ({$string})";
            } else {
                $where[] = "a_cop.SupplierSKUID in ({$string})";
            }
        }
        
        //supplier_skucodes
        if( !empty($param['supplier_skucodes']) ) {
            $string = "'" . implode("','", $param['supplier_skucodes']) . "'";
            $where[] = "a_cop.SupplierSKUCode in ({$string})";
        }
        
        //skucodes
        if( !empty($param['skucodes']) ) {
            $string = "'" . implode("','", $param['skucodes']) . "'";
            $where[] = "com.SKUCode in ({$string})";
        }
        
        //goods_name
        if( !empty($param['goods_name']) ) {
            $goods_name = $param['goods_name'];
            $_get_multi_search = getMultiLikeByString("'",$goods_name,'com.Name');
            if( ! $_get_multi_search ) {
                $where[] = "com.Name like '%{$goods_name}%'";
            } else {
                $where[] = $_get_multi_search;
            }
        }
        
        //skucode
        if( !empty($param['skucode']) ) {
            $where[] = "com.SkuCode = '{$param['skucode']}'";
        }
        
        //barcode
        if( !empty($param['barcode']) ) {
            $where[] = "com.BarCode = '{$param['barcode']}'";
        }
        
        //barcodes
        if( !empty($param['barcodes']) ) {
            $string = "'" . implode("','", $param['barcodes']) . "'";
            $where[] = "com.BarCode in ({$string})";
        }
        
        //has_rate
        if( isset($param['has_rate']) && in_array($param['has_rate'],['0','1']) ) {
            $has_rate = (int)$param['has_rate'];
            $where[] = "scb.IsHaveTaxRate = {$has_rate}";
        }
        
        //is_free_postfree
        if( isset($param['is_free_postfree']) && in_array($param['is_free_postfree'],['0','1'])  ) {
            $is_free_postfree = (int)$param['is_free_postfree'];
            $where[] = "scb.IsFreeShipping = {$is_free_postfree}";
        }
        
        //skuids
        if( !empty($param['skuids']) ) {
            $string = implode(",", $param['skuids']);
            $where[] = "a_cop.SkuID IN({$string})";
        }
        
        //class_ids
        if( !empty($param['class_ids']) ) {
            $string = implode(",", $param['class_ids']);
            $where[] = "( com.FirstClassificationID IN({$string}) or com.SecondClassificationID IN({$string}) or com.ThirdClassificationID IN({$string}))";
        }
        
        //brand_ids
        if( !empty($param['brand_ids']) ) {
            $string = implode(",", $param['brand_ids']);
            $where[] = "com.BrandID IN({$string})";
        }
        
        //country_ids
        if( !empty($param['country_ids']) ) {
            $string = implode(",", $param['country_ids']);
            $where[] = "com.CountryID IN({$string})";
        }
        
        //commodity_types
        if( !empty($param['commodity_types']) ) {
            $string = implode(",", $param['commodity_types']);
            $where[] = "com.CommodityType IN({$string})";
        }

        //goods_ids
        if( !empty($param['goods_ids']) ) {
            $string = implode(",", $param['goods_ids']);
            $where[] = "com.ID IN({$string})";
        }

        //supplier_ids
        if( !empty($param['supplier_ids']) ) {
            $string = implode(",", $param['supplier_ids']);
            $where[] = "scb.SupplierCompanyID IN({$string})";
        }

        //whereOr // 多个条件搜索
        if( !empty($param['whereOr']) ) {
            $string_arr = [];
            foreach ($param['whereOr'] as $whereKey => $whereOr){
                $string_arr[] = $whereKey . ' '. $whereOr['method'] . ' "' . $whereOr['value'].'" ';
            }
            $string = implode(" OR ", $string_arr);
            $where[] = "( {$string} )";
        }
        return Normal::returnCode(Normal::SUC,"获取成功",$where);
    }
    
    /**
     * 获取对外推送授权渠道模板数据字段
     * @var array
     */
    protected function getGoodsColumns()
    {
        return [
            Db::raw("CONCAT(a_cop.ID,'') as cop_id"),
            Db::raw("CONCAT(com.ID,'') as GoodsID"),
            "com.Name",
            "cds.Images",
            "cds.Description",
            "cds.Video",
            "com.CommodityType",
            "com.BarCode",
            Db::raw("IFNULL(com.FreightTemplateID,0) as FreightTemplateID"),
            Db::raw("IFNULL(com.Weight,0.000) as Weight"),
            "com.WeightUnit",
            Db::raw("CONCAT(com.SpuID,'') as SpuID"),
            Db::raw("IFNULL(com.SpuStatus,'') as SpuStatus"),
            Db::raw("IFNULL(com.SpuDefault,'') as SpuDefault"),
            Db::raw("IFNULL(com.SpecValue,'') as SpecValue"),
            Db::raw("IFNULL(com.SpuSpecJsonBody,'') as SpuSpecJsonBody"),
            Db::raw("IFNULL(com.MarketPrice,0.00) as MarketPrice"),
            Db::raw("CONCAT(com.ID,'') AS SkuID"),
            Db::raw("CONCAT(com.SKUCode,'') AS SKUCode"),
            Db::raw("CONCAT(stock.SupplierCommodityID,'') AS SupplierCommodityID"),
            "scb.SupplierCommoditySkuCode",
            Db::raw("IFNULL(scb.PlatSkuCode,'') as PlatSkuCode"),
            Db::raw("CONCAT(stock.BatchID,'') AS BatchID"),
            Db::raw("CONCAT(stock.ID,'') AS StockInfoID"),
            "stock.BatchNo",
            Db::raw("IFNULL(stock.LimitBuyCount,0) AS LimitBuyCount"),
            Db::raw("IFNULL(stock.CalibrationPeriodTime,'') as CalibrationPeriodTime"),
            "stock.Number",
            "stock.IsDefective",
            "stock.NumberLock",
            "scb.IsFreeShipping",
            "scb.IsNeedIDCard",
            "scb.IsNeedUploadIDCard",
            "scb.IsNeedWeiXinPay",
            "scb.DefaultStock",
            "scb.DeclarePlanCode",
            "scb.DeliveryMethod",
            "scb.IsHaveTaxRate",
            "scb.SupplierCommoditySpec",
            Db::raw("IFNULL(scb.TaxRate,0.0000) as TaxRate"),
            Db::raw("IFNULL(scb.TaxInComeRate,0.0000) as TaxInComeRate"),
            Db::raw("IFNULL(scb.HSCode,'') as HSCode"),
            "scb.PriceControlType",
            "scb.LimitOrControlPrice",
            "scb.IsInsurance",
            "scb.InsurancePriceDay",
            Db::raw("CONCAT(cop.ID,'') as CopID"),
            "a_cop.SpecList",
            "cop.IsPriceSet",
            "cop.PriceScaleRule",
            "cop.PriceRuleType",
            "cop.IsAuth",
            "cop.IsInit",
            "cop.BatchSpecMinSinglePrice",
            Db::raw("CONCAT(a_cop.StockInfoID,'') as CopStockInfoID"),
            Db::raw("CONCAT(cop.BatchSpecMinSinglePrice,'') as CopBatchSpecMinSinglePrice"),
            Db::raw("CONCAT(cop.BatchNo,'') as CopBatchNo"),
            Db::raw("CONCAT(w.ID,'') as WarehouseID"),
            "w.WarehouseAccount",
            "w.WarehouseCode",
            "w.WarehouseName",
            "w.WarehouseType",
            Db::raw("IFNULL(w.IsCombineOrders,0) as IsCombineOrders"),
            "w.JsonBody",
            Db::raw("CONCAT(s.ID,'') as SupplierID"),
            "s.SupplierName",
            "s.SupplierCode",
            "s.SupplierAlias",
            "s.OrderDeclare",
            "s.PayDeclare",
            "s.SettlementMethod",
            "s.OrderDockMode",
            "s.CompanyStatus",
            "s.IsWholesaler",
            "s.ThirdPartyPlatformCode",
            "s.BusinessLicenseNo",
            "s.FastDeliveryType",
            Db::raw("IFNULL(s.EnterpriseName,'') as EnterpriseName"),
            Db::raw("IFNULL(s.EnterpriseCode,'') as EnterpriseCode"),
            Db::raw("CONCAT(f_class.ID,'') as FirstClassificationID"),
            Db::raw("IFNULL(f_class.ExtraID,0) as FirstClassExtraID"),
            "f_class.ClassificationCode as FirstClassificationCode",
            "f_class.ClassificationName as FirstClassificationName",
            Db::raw("CONCAT(s_class.ID,'') as SecondClassificationID"),
            Db::raw("IFNULL(s_class.ExtraID,0) as SecondClassExtraID"),
            "s_class.ClassificationCode as SecondClassificationCode",
            "s_class.ClassificationName as SecondClassificationName",
            Db::raw("CONCAT(t_class.ID,'') as ThirdClassificationID"),
            Db::raw("IFNULL(t_class.ExtraID,0) as ThirdClassExtraID"),
            "t_class.ClassificationCode as ThirdClassificationCode",
            "t_class.ClassificationName as ThirdClassificationName",
            Db::raw("CONCAT(b.ID,'') AS BrandID"),
            Db::raw("IFNULL(b.ExtraID,0) AS BrandExtraID"),
            "b.BrandCode",
            "b.BrandName",
            Db::raw("CONCAT(c.ID,'') AS CountryID"),
            Db::raw("IFNULL(c.ExtraID,0) AS CountryExtraID"),
            "c.CountryCode",
            "c.CountryName",
            Db::raw("CONCAT(u.ID,'') as UnitID"),
            Db::raw("IFNULL(u.ExtraID,0) as UnitExtraID"),
            "u.UnitCode",
            "u.Name as UnitName",
            "scb_cus.Country as DeclareCountry",
            "scb_cus.CountryCode AS DeclareCountryCode",
            "scb_cus.Currency as DeclareCurrency",
            "scb_cus.CurrencyCode as DeclareCurrencyCode",
            "scb_cus.DeclareBarCode",
            "scb_cus.DeclareCommodityName",
            "scb_cus.DeclareSKUCode",
            "scb_cus.Function as DeclareFunction",
            "scb_cus.MeasurementUnit as DeclareMeasurementUnit",
            "scb_cus.MeasurementUnitCode as DeclareMeasurementUnitCode",
            "scb_cus.Specifications as DeclareSpecifications",
            "scb.CommodityCommissionLevelJsonBody as CommissionLevelList",
            "scb.LimitPriceMode",
            "scb.LimitPriceScale",
            'scb.ConSumptionTaxRate',
            'scb.ConSumptionPrice',
            'scb.CustomID',
            'scb.IsCombineSupplierCommodity',
            'scb.CombineGoodsContents',
            'csp.SpuName',
            'csp.ExtraID as SpuExtraID'
        ];
    }   

    /**
     * 获取模板数据字段
     */
    protected function getGoodsBaseColumns()
    {
        return [
            Db::raw("CONCAT(com.ID,'') as GoodsID"),
            "com.Name",
            "cds.Images",
            "cds.Description",
            "com.CommodityType",
            "com.BarCode",
            Db::raw("IFNULL(com.FreightTemplateID,0) as FreightTemplateID"),
            Db::raw("IFNULL(com.Weight,0.000) as Weight"),
            "com.SpuID",
            Db::raw("IFNULL(spu.SpuName,'') as SpuName"),
            Db::raw("IFNULL(spu.SpuCode,'') as SpuCode"),
            Db::raw("IFNULL(com.MarketPrice,0.00) as MarketPrice"),
            Db::raw("IFNULL(com.SpuSpecJsonBody,'') AS SpuSpecJsonBody"),
            Db::raw("CONCAT(com.ID,'') AS SkuID"),
            Db::raw("CONCAT(com.SKUCode,'') AS SKUCode"),
            Db::raw("CONCAT(stock.SupplierCommodityID,'') AS SupplierCommodityID"),
            "scb.SupplierCommoditySkuCode",
            Db::raw("CONCAT(stock.BatchID,'') AS BatchID"),
            Db::raw("CONCAT(stock.ID,'') AS StockInfoID"),
            "stock.BatchNo",
            Db::raw("IFNULL(stock.LimitBuyCount,0) AS LimitBuyCount"),
            Db::raw("IFNULL(stock.CalibrationPeriodTime,'') as CalibrationPeriodTime"),
            "stock.Number",
            "stock.IsDefective",
            "stock.NumberLock",
            "scb.IsFreeShipping",
            "scb.IsNeedIDCard",
            "scb.IsNeedUploadIDCard",
            "scb.IsNeedWeiXinPay",
            "scb.DefaultStock",
            "scb.DeclarePlanCode",
            "scb.DeliveryMethod",
            "scb.IsHaveTaxRate",
            "scb.SupplierCommoditySpec",
            Db::raw("IFNULL(scb.TaxRate,0.0000) as TaxRate"),
            Db::raw("IFNULL(scb.HSCode,'') as HSCode"),
            "scb.PriceControlType",
            "scb.LimitOrControlPrice",
            Db::raw("CONCAT(cop.ID,'') as CopID"),
            "cop.SpecList",
            "cop.IsPriceSet",
            "cop.PriceScaleRule",
            "cop.PriceRuleType",
            "cop.IsAuth",
            "cop.IsInit",
            "cop.BatchSpecMinSinglePrice",
            Db::raw("CONCAT(cop.StockInfoID,'') as CopStockInfoID"),
            Db::raw("CONCAT(cop.BatchSpecMinSinglePrice,'') as CopBatchSpecMinSinglePrice"),
            Db::raw("CONCAT(cop.BatchNo,'') as CopBatchNo"),
            Db::raw("CONCAT(w.ID,'') as WarehouseID"),
            "w.WarehouseAccount",
            "w.WarehouseCode",
            "w.WarehouseName",
            "w.WarehouseType",
            Db::raw("IFNULL(w.IsCombineOrders,0) as IsCombineOrders"),
            "w.JsonBody",
            Db::raw("CONCAT(s.ID,'') as SupplierID"),
            "s.SupplierName",
            "s.SupplierCode",
            "s.SupplierAlias",
            "s.OrderDeclare",
            "s.PayDeclare",
            "s.SettlementMethod",
            "s.OrderDockMode",
            "s.CompanyStatus",
            "s.IsWholesaler",
            "s.ThirdPartyPlatformCode",
            "s.BusinessLicenseNo",
            "s.FastDeliveryType",
            Db::raw("IFNULL(s.EnterpriseName,'') as EnterpriseName"),
            Db::raw("IFNULL(s.EnterpriseCode,'') as EnterpriseCode"),
            Db::raw("CONCAT(f_class.ID,'') as FirstClassificationID"),
            "f_class.ClassificationCode as FirstClassificationCode",
            "f_class.ClassificationName as FirstClassificationName",
            Db::raw("CONCAT(s_class.ID,'') as SecondClassificationID"),
            "s_class.ClassificationCode as SecondClassificationCode",
            "s_class.ClassificationName as SecondClassificationName",
            Db::raw("CONCAT(t_class.ID,'') as ThirdClassificationID"),
            "t_class.ClassificationCode as ThirdClassificationCode",
            "t_class.ClassificationName as ThirdClassificationName",
            Db::raw("CONCAT(b.ID,'') AS BrandID"),
            "b.BrandCode",
            "b.BrandName",
            Db::raw("CONCAT(c.ID,'') AS CountryID"),
            "c.CountryCode",
            "c.CountryName",
            Db::raw("CONCAT(u.ID,'') as UnitID"),
            "u.UnitCode",
            "u.Name as UnitName",
            "scb_cus.Country as DeclareCountry",
            "scb_cus.CountryCode AS DeclareCountryCode",
            "scb_cus.Currency as DeclareCurrency",
            "scb_cus.CurrencyCode as DeclareCurrencyCode",
            "scb_cus.DeclareBarCode",
            "scb_cus.DeclareCommodityName",
            "scb_cus.DeclareSKUCode",
            "scb_cus.Function as DeclareFunction",
            "scb_cus.MeasurementUnit as DeclareMeasurementUnit",
            "scb_cus.MeasurementUnitCode as DeclareMeasurementUnitCode",
            "scb_cus.Specifications as DeclareSpecifications",
            "scb.CommodityCommissionLevelJsonBody as CommissionLevelList",
            "scb.LimitPriceMode",
            "scb.LimitPriceScale",
            'scb.ConSumptionTaxRate',
            'scb.ConSumptionPrice',
            'scb.CustomID'
        ];
    }
    
    /**
     * 获取推送数据的价格模板处理
     * @param array $param['binfo'] 商户信息
     * @param array $param['tplInfo']  Template表指定价格模板id的数据
     * @param array $param['goodsList']  商品sku信息
     * @return mixed
     */
    public function getUserAuthGoodsList(array $param = [])
    {
        if( empty($param['binfo']) ){
            return Normal::returnCode(Normal::FAIL,'参数缺失binfo');
        }
        
        if( empty($param['tplInfo']) ){
            return Normal::returnCode(Normal::FAIL,'参数缺失tplInfo');
        }
        
        if( empty($param['goodsList']) ){
            return Normal::returnCode(Normal::FAIL,'参数缺失goodsList');
        }
        
        $goodsList = $param['goodsList'];
        $binfo     = $param['binfo'];
        $tplInfo   = $param['tplInfo'];
        
        //是否给下游统一推送建议零售价
        $is_market_price_send = $binfo['is_market_price_send'];
        $dataList = [];
        foreach ($goodsList as $skuInfo )
        {
            //处理规格问题
            $spec = $this->getGoodsSpecList($skuInfo,$tplInfo,$binfo);
            $specList = $spec['SpecList'];
            $specList = array_map(function( & $value ) use ($is_market_price_send){
                unset($value['priceCost']);
                if( $is_market_price_send == 1 ) {
                    $value['priceSettlement'] = $value['price'];
                }
                return $value;
            }, $specList);
            $dataList[] = [
                'goods_id'  => $skuInfo['SkuID'],
                'skucode'   => $skuInfo['skuCode'],
                'barcode'   => $skuInfo['BarCode'],
                'spec_list' => json_encode($specList,JSON_UNESCAPED_UNICODE),
            ];            
        }
        
        return Normal::returnCode(Normal::SUC,'获取成功',$dataList);
    }
    
    /**
     * 处理下游标准规格结构
     * @param array $skuInfo
     * @param array $priceInfo
     */
    public function getGoodsSpecList($skuInfo = [] ,$priceInfo = [],$bInfo = [])
    {
        if( is_string($skuInfo['SpecList']) ) {
            $specList = @json_decode($skuInfo['SpecList'],true);
        } else {
            $specList = $skuInfo['SpecList'];
        }
        
        $newSpecList = [];
        foreach ($specList as $item )
        {
            $isPriceControl     = in_array($skuInfo['PriceControlType'],[1,2]) ? true : false ;
            $controlPrice       = (float)$skuInfo['LimitOrControlPrice'];
            $specificationNum   = (int)($item['SpecificationNum'] ?? $item['SpecNum']);
            $specificationPrice = (float)($item['SpecFreightPrice'] ?? $item['SpecPrice'] ?? $item['SpecificationFreightPrice'] ?? $item['SpecificationPrice']);
            $marketPrice        = $skuInfo['MarketPrice'];//市场单价
            $singleSpecPrice    = bcdiv((string)$specificationPrice, (string)$specificationNum,2);
            $item['LimitOrControlPrice'] = empty($item['LimitOrControlPrice']) ? NULL : $item['LimitOrControlPrice'];


            $spec = [
                "spec_num"    	=> $specificationNum,
                "specName"    	=> $item['specName'] ?? $specificationNum.$skuInfo['UnitName'],
                "expired_date"	=> $this->isDateTime($skuInfo['CalibrationPeriodTime']) ? date("Y-m-d",strtotime($skuInfo['CalibrationPeriodTime'])) : '',
                "make_date"   	=> '',
                "price"       	=> $specificationPrice,
                "priceCost"   	=> (float)$specificationPrice,
                "priceControl"	=> $item['LimitOrControlPrice']?? bcmul((string)$specificationNum,(string)$controlPrice,2),
                "priceVip"      => $item['VipPirce'] ?? "",
                'singlePrice' 	=> bcmul((string)$singleSpecPrice, '100'),
            ];
    
            $PriceRuleTypeFixed = ($skuInfo['PriceRuleType'] == 2 && isset($item['price_rate']) && isset($item['price_settlement_rate']) && isset($item['price_market_rate']));
            //根据设置的比例或者导入的价格计算
            if( $skuInfo['PriceRuleType'] == 1 ) {//独立的价格比列
                $priceInfo = @json_decode($skuInfo['PriceScaleRule'],true);
                $calc_spec = $this->calcPriceByTemplate($skuInfo,$priceInfo,$spec);
            } else if( $PriceRuleTypeFixed ) {//独立设置价格
                $calc_spec = [
                    'price'             => $item['price_rate'],
                    'priceSettlement'   => $item['price_settlement_rate'],
                    'priceMarket'       => $item['price_market_rate'],
                ];
            } else {
                $calc_spec = $this->calcPriceByTemplate($skuInfo,$priceInfo,$spec);
            }
    
            //限价、控价商品乘以对应的规格数量
            if( $isPriceControl ) {
                $limitOrControlPrice = null;
                //判断价格模式
                if($skuInfo['LimitPriceMode']==1){
                    $limitOrControlPrice=bcmul((string)$specificationNum,(string)$controlPrice,2);
                }elseif($skuInfo['LimitPriceMode']==2){
                    $limitOrControlPrice =  bcadd((string)$specificationPrice,bcmul((string)$skuInfo['LimitPriceScale'], (string)$specificationPrice, 2), 2);
                }
                $calc_spec = [
                    'price'             => $item['LimitOrControlPrice'] ?? $limitOrControlPrice ?? $specificationPrice,
                    "priceControl"	    => $item['LimitOrControlPrice'] ?? $limitOrControlPrice ?? $specificationPrice,
                    'priceSettlement'   => $calc_spec['priceSettlement'],
                    'priceMarket'       => $calc_spec['priceMarket'],
                ];
            }
            
            //如果设置了建议零售价，统一乘以对应的规格值进行计算
            if( !empty($skuInfo['MarketPrice']) && $skuInfo['MarketPrice'] > 0 )
            {
                $calc_spec['priceMarket'] = bcmul((string)$skuInfo['MarketPrice'],(string)$specificationNum,2);
                if (!$isPriceControl){
                    $calc_spec['price'] = $calc_spec['priceMarket'];
                }
            }

            //得到规格组单商品价格
            $singleSpecPrice = bcdiv((string)$calc_spec['priceSettlement'], (string)$specificationNum,2);
            
            
            //判断成本价是否触发高税率
            $taxModeRateInfo = $this->getTaxRate(
                (string)$singleSpecPrice,
                (int)$skuInfo['IsHaveTaxRate'],
                (string)$skuInfo['TaxRate'],
                (string)$skuInfo['ConSumptionTaxRate'],
                (string)$skuInfo['ConSumptionPrice']
            );
            
            
            //是否触发高税
            $calc_spec['isHighTax'] = $taxModeRateInfo['is_high_tax'];
            
            //非固定价格的计算方式
            if(!$PriceRuleTypeFixed){
                //是否向下游推送固定含税
                $calc_spec['priceSettlement'] = $this->changeTaxPrice(
                    (string)$calc_spec['priceSettlement'],
                    (int)$bInfo['user_tax_mode'],
                    (int)$skuInfo['IsHaveTaxRate'],
                    (array)$taxModeRateInfo,
                    (string)$skuInfo['TaxRate']
                );
            }
            
            //合并计算后的价格
            $newSpecList[] = array_merge($spec,$calc_spec);
        }
    
        //寻找最大值和最小值
        $singlePrices  = array_column($newSpecList, 'singlePrice');
        $minPrice      = min($singlePrices);
        $isMinStockSpecificationItem = false;
        foreach ($newSpecList as & $item )
        {
            if( $item['singlePrice'] == $minPrice && $isMinStockSpecificationItem == false ) {
                $item['isMinStockSpecificationItem'] = true;
            } else {
                $item['isMinStockSpecificationItem'] = false;
            }
            unset($item['singlePrice']);
        }
    
        return [
            'minSinglePrice'    => $minPrice,
            'maxSinglePrice'    => max($singlePrices),
            'SpecList'          => $newSpecList,
            'taxRate'           => $skuInfo['TaxRate']
        ];
    }

    public function isDateTime($dateTime,$formats=['Y-m-d','Y/m/d','Y-m-d H:i:s','Ymd'])
    {
        if ($dateTime=='0000-00-00 00:00:00')
        {
            return false;
        }
        $unixTime = strtotime($dateTime);
        if (!$unixTime){ //strtotime转换不对，日期格式显然不对。
            return false;
        }
        //校验日期的有效性，只要满足其中一个格式就OK
        foreach ($formats as $format){
            if (date($format,$unixTime)==$dateTime){
                return true;
            }
        }
        return false;
    }
    
    /**
     * 根据不同价格模板计算价格
     * @param array $skuInfo
     * @param array $price_contents
     * @param array $spec
     * @return mixed
     */
    public function calcPriceByTemplate( $skuInfo = [] , $price_contents = [] , $spec = [] )
    {
        //商品成本规格价格
        $spec_price             = $spec['price'];
        $price_rate             = $price_contents['price_rate'];
        $price_settlement_rate  = $price_contents['price_settlement_rate'];
        $price_market_rate      = $price_contents['price_market_rate'];
        return [
            'price'             => $this->calcPriceFloat(bcmul((string)$spec_price, bcadd((string)self::BASE_PRICE_RATE,bcdiv((string)$price_rate,'100',self::BASE_SCALE_TYPE),self::BASE_SCALE_TYPE),self::BASE_FLOAT)),
            'priceSettlement'   => $this->calcPriceFloat(bcmul((string)$spec_price, bcadd((string)self::BASE_PRICE_RATE,bcdiv((string)$price_settlement_rate,'100',self::BASE_SCALE_TYPE),self::BASE_SCALE_TYPE),self::BASE_FLOAT)),
            'priceMarket'       => $this->calcPriceFloat(bcmul((string)$spec_price, bcadd((string)self::BASE_PRICE_RATE,bcdiv((string)$price_market_rate,'100',self::BASE_SCALE_TYPE),self::BASE_SCALE_TYPE),self::BASE_FLOAT)),
        ];
        
    }

    /**
     * 价格计算向上取值
     * @param $price
     * @return string|null
     */
    public function calcPriceFloat($price)
    {
        $mulPrice = ceil(bcmul((string)$price,'100',1));
        return bcdiv((string)$mulPrice,'100',2);
    }
    
    /**
     * 根据填写的包邮模型来修改价格
     * @param strint $price
     * @param int $taxMode
     * @param int $isHaveTax
     * @param array $taxRateInfo
     * @param string $lowTaxRate
     */
    public function changeTaxPrice(string $price, int $taxMode,int $isHaveTax,array $taxRateInfo,string $lowTaxRate)
    {
        $tax_rate       = bcadd((string)$taxRateInfo['tax_rate'],'1.0000',4);
        $low_tax_rate   = bcadd((string)$lowTaxRate,'1.0000',4);

        //含税
        if( $isHaveTax == 1 ) {
            
            //商品含税
            if( $taxMode == 2 )
            {
                if($taxRateInfo['price_status'] == 2){
                    //不含税含税价格 
                    $price = $this->calcPriceFloat(bcdiv((string)$price,$tax_rate,self::BASE_FLOAT));
                }else{
                    //不含税价格
                    $price = $this->calcPriceFloat(bcdiv((string)$price,$low_tax_rate,self::BASE_FLOAT));
                }
            }
            else
            {
                //含税商品 推含税下去
                if( $taxRateInfo['price_status'] == 1 ) {
                    //含税价格 重新计算税率
                    $price = $this->calcPriceFloat(bcdiv((string)$price,$low_tax_rate,self::BASE_FLOAT));
                    $price = $this->calcPriceFloat(bcmul((string)$price,$tax_rate,self::BASE_FLOAT));
                }
            }
        }
        else
        {
            //商品不含税
            if($taxMode == 1){
                $price = $this->calcPriceFloat(bcmul((string)$price,$tax_rate,self::BASE_FLOAT));
            }
        }
        return $price;
    }

    /**
     * 计算商品毛利编码
     * @param float $MarketPrice
     * @param float $MaxSinglePrice
     * @param array $goodsLevelConfig
     */
    public function getGoodsLevel(float $MarketPrice, float $MaxSinglePrice,$goodsLevelConfig)
    {
        $MarketPrice    = bcdiv((string)$MarketPrice,'100',2);
        $MaxSinglePrice = bcdiv((string)$MaxSinglePrice,'100',2);
        $bcsub_value    = bcsub((string)$MarketPrice, (string)$MaxSinglePrice,2);
        krsort($goodsLevelConfig);
    
        $lv = '';
        foreach ($goodsLevelConfig as $level_price => $goods_level )
        {
            if( $bcsub_value >= $level_price ) {
                $lv = $goods_level;
                break;
            }
        }
    
        $last = end($goodsLevelConfig);
        if( !$lv ) {
            $lv = $last;
        }
        return $lv;
    }
    
    /**
     * 处理图片
     * 区分主图 为第一张
     * @param array $images
     */
    public function getResetImagesList( $images = [] )
    {
        if( empty($images) ) {
            return [];
        }
        $isMainUrls = [];
        $otherUrls = [];
        foreach ($images as $item )
        {
            if( $item['IsMain'] == 1 && empty($isMainUrls) ) {
                $isMainUrls[] = $item['ImageUrl'];
            } else {
                $otherUrls[] = $item['ImageUrl'];
            }
        }
    
        return array_merge($isMainUrls,$otherUrls);
    }
    
    /**
     * 获取云仓商城
     * 下单调度动态切换供应商商品，最大限度保证下单成功
     * 如果当前批次下单不成功的话，会切换到优选里面满足条件的其它供应商商品
     * 优先售卖为先，之后价格最低为先，同时满足规格数量能和下单一致就可以进行自动切换
     * @param int $param['bid'] 渠道用户ID
     * @param array $param['goods_list'] 下单商品组
     * @param bool $param['isBbcOrder'] 是否BBC订单
     * @param int $param['is_router_user'] 是否路由模式商品
     */
    public function getRealGoodsList( array $param = [] )
    {
        if( !isset($param['bid']) ) {
            return Normal::returnCode(Normal::FAIL,'参数缺失BID');
        }
        
        if( empty($param['goods_list']) ) {
            return Normal::returnCode(Normal::FAIL,'参数缺失goods_list');
        }
        
        $param['is_router_user'] = $is_router_user = isset($param['is_router_user']) ? $param['is_router_user'] : 0 ;
        $param['isBbcOrder']     = isset($param['isBbcOrder']) ? $param['isBbcOrder'] : false ;
        
        
        if( $param['is_router_user'] == 0 ) {
            $SkuIds = array_column($param['goods_list'],'goods_id');
        } else {
            $SkuIds = array_column($param['goods_list'],'skucode');//编码当成ID
        }
        
        //实际调度的商品
        $dispatchBackUpGoodsList = $this->getDispatchOrderGoodsList($param['bid'],$SkuIds,$param['is_router_user']);
        $orderDispatchGoods = [];
        foreach ($param['goods_list'] as $goods_item )
        {
            $SkuId      = $goods_item['goods_id'];
            $batch_no   = $goods_item['batch_no'];
            $spec_num   = $goods_item['spec_num'];
            $goods_num  = $goods_item['goods_num'];
            $buy_number = $goods_num * $spec_num;
            if( !isset($dispatchBackUpGoodsList[$SkuId]) ) {
                
                if( $param['isBbcOrder'] ) {
                    return Normal::returnCode(Normal::FAIL,"当前优选商品『{$goods_item['skucode']}』已下架,请重新选购商品！100000");
                }
                
                return Normal::returnCode(Normal::FAIL,"当前优选商品『{$goods_item['goods_name']}』已下架,请重新选购商品！100001");
            }
            
            $currentGoods = $dispatchBackUpGoodsList[$SkuId];
            //优选售卖商品
            $saleFirstGoods = $currentGoods[1] ?? [];
            if( !empty($saleFirstGoods) ) {
                //寻找满足条件的数据
                foreach ($saleFirstGoods as $item )
                {
                    $SpecList = array_column($item['SpecList'],'SpecificationPrice','SpecificationNum');
                    $Number   = $item['number'];
                    //满足库存和规格
                    if( ($Number >= $buy_number && isset($SpecList[$spec_num]) && $is_router_user == 0) || $is_router_user == 1 ) {
                        $orderDispatchGoods[$SkuId] = $item;
                    }
                }
            }
    
            //正常优选商品
            $saleNormalGoods = $currentGoods[0] ?? [] ;
            if( !empty($saleNormalGoods) )
            {
                //寻找满足条件的数据
                foreach ($saleNormalGoods as $item )
                {
                    $SpecList = array_column($item['SpecList'],'SpecificationPrice','SpecificationNum');
                    $Number   = $item['number'];
                    //满足库存和规格
                    if( (!isset($orderDispatchGoods[$SkuId]) && $Number >= $buy_number && isset($SpecList[$spec_num]) && $is_router_user == 0 )  || $is_router_user == 1) {
                        $orderDispatchGoods[$SkuId] = $item;
                    }
                }
            }
            
            //未找到满足条件的供应商商品
            if( empty($orderDispatchGoods[$SkuId]) ) {
                if( $param['isBbcOrder'] ) {
                    return Normal::returnCode(Normal::FAIL,"当前优选商品『{$goods_item['skucode']}』已下架,请重新选购商品！100002");
                }
                return Normal::returnCode(Normal::FAIL,"当前优选商品『{$goods_item['goods_name']},{$goods_item['skucode']}』已下架,请重新选购商品！100003");
            }
    
            unset($item);
        }
        return Normal::returnCode(Normal::SUC,"获取优选商品成功",$orderDispatchGoods);
    }
    
    /**
     * 获取是否触发高税价格税率配置组
     * 1、如果是供应商商品含税的，要将预警单价乘以预警税率之后 再和 含税单价进行比较
     * 2、如果是供应商商品不含税的，直接比较预警单价
     * @param string $price 规格单价
     * @param string $has_rate 是否含税
     * @param string $tax_rate 供应商商品税率
     * @param string $con_tax_rate 预警税率
     * @param string $con_price 预警单价（不含税）
     * @return mixed
     */
    public function getTaxRate(string $price,int $has_rate,string $tax_rate,string $con_tax_rate,string $con_price)
    {
        if( $has_rate == 1 ) 
        {
            //商品单价默认都是含税 要把不含税的价格算出来
            $percent      = bcadd('1', $tax_rate, 4);
            $high_percent = bcadd('1', $con_tax_rate, 4);
            
            // $notTaxPrice  = bcdiv($price, $percent, 3);
            // $notTaxPrice2 = bcmul($notTaxPrice, '100', 2);
            // $notTaxPrice3 = ceil($notTaxPrice2);
            // $price        = bcdiv((string)$notTaxPrice3, '100', 2);
            
            //新逻辑含税使用 预警价格乘于税率 
            $con_low_price  = bcmul($con_price,$percent,2);
            //高税预警价
            $con_high_price = bcmul($con_price,$high_percent,2);
            
            //如果设置了是否高税
            if ( $con_tax_rate > 0 && $con_price > 0 ) 
            {
                if ( bccomp((string)$price, (string)$con_low_price, 2) >= 0 ) 
                {
                    if( bccomp((string)$price, (string)$con_high_price, 2) >= 0 )
                    {
                        return [
                            'tax_rate'      => $con_tax_rate,
                            'is_high_tax'   => 1,
                            'price_status'  => 2, //这里需要直接除于高税
                        ];
                    }
                    
                    return [
                        'tax_rate'      => $con_tax_rate,
                        'is_high_tax'   => 1,
                        'price_status'  => 1, //这里需要先除于低税,先乘高税
                    ];
                }
            }
            
            return [
                'tax_rate'      => $tax_rate,
                'is_high_tax'   => 0,
                'price_status'  => 0, //这里需要直接除于低税,没有触发
            ];
        
        }
        else
        {
            //不含税直接比较
            if ( $con_tax_rate > 0 && $con_price > 0 && ( bccomp((string)$price, (string)$con_price, 2) >= 0) ) 
            {
                return [
                    'tax_rate'      => $con_tax_rate,
                    'is_high_tax'   => 1,
                    'price_status'  => 3, //这里需要直接除于低税
                ];
            }
            
            return [
                'tax_rate'      => $tax_rate,
                'is_high_tax'   => 0,
                'price_status'  => 3, //这里需要直接除于低税
            ];
        }
    }
    
    /**
     * 获取三方渠道下单商品调度
     * 下单调度动态切换供应商商品，最大限度保证下单成功
     * 如果当前批次下单不成功的话，会切换到优选里面满足条件的其它供应商商品
     * 优先售卖为先，之后价格最低为先，同时满足规格数量能和下单一致就可以进行自动切换
     * @param int $param['bid'] 渠道用户ID
     * @param array $param['goods_list'] 下单商品组
     * @param bool $param['isBbcOrder'] 是否BBC订单
     * @param int $param['is_router_user'] 是否路由模式商品
     */
    public function getOrderRealGoodsList( array $param = [] )
    {
        if( !isset($param['bid']) ) {
            return Normal::returnCode(Normal::FAIL,'参数缺失BID');
        }
        
        if( empty($param['goods_list']) ) {
            return Normal::returnCode(Normal::FAIL,'参数缺失goods_list');
        }
        
        $param['is_router_user'] = $is_router_user = isset($param['is_router_user']) ? $param['is_router_user'] : 0 ;
        $param['isBbcOrder']     = isset($param['isBbcOrder']) ? $param['isBbcOrder'] : false ;
        $orderType = isset($param['orderType']) ? $param['orderType'] : 0 ;
        
        if( $param['is_router_user'] == 0 ) {
            $SkuIds = array_column($param['goods_list'],'goods_id');
        } else {
            $SkuIds = array_column($param['goods_list'],'skucode');//编码当成ID
        }
        
        //实际调度的商品
        $dispatchBackUpGoodsList = $this->getDispatchOrderGoodsList($param['bid'],$SkuIds,$param['is_router_user']);
        $orderDispatchGoods = [];
        foreach ($param['goods_list'] as $goods_item )
        {
            $SkuId      = $goods_item['goods_id'];
            $batch_no   = $goods_item['batch_no'];
            $spec_num   = $goods_item['spec_num'];
            $goods_num  = $goods_item['goods_num'];
            $buy_number = $goods_num * $spec_num;
            if( !isset($dispatchBackUpGoodsList[$SkuId]) ) {
                
                if( $param['isBbcOrder'] ) {
                    return Normal::returnCode(Normal::FAIL,"当前优选商品『{$goods_item['skucode']}』已下架,请重新选购商品！100000");
                }
                
                return Normal::returnCode(Normal::FAIL,"当前优选商品『{$goods_item['goods_name']}』已下架,请重新选购商品！100001");
            }
            
            $currentGoods = $dispatchBackUpGoodsList[$SkuId];
            //优选售卖商品
            $saleFirstGoods = $currentGoods[1] ?? [];
            if( !empty($saleFirstGoods) ) {
                //寻找满足条件的数据
                foreach ($saleFirstGoods as $item )
                {
                    $SpecList = array_column($item['SpecList'],'SpecificationPrice','SpecificationNum');
                    $Number   = $item['number'];
                    //满足库存和规格
                    if( ( ( !in_array($orderType,[9,10]) && $Number >= $buy_number && $is_router_user == 0 ) || in_array($orderType,[9,10]) || $is_router_user == 1) && isset($SpecList[$spec_num]) ) {
                        $orderDispatchGoods[$SkuId] = $item;
                    }
                }
            }
    
            //正常优选商品
            $saleNormalGoods = $currentGoods[0] ?? [] ;
            if( !empty($saleNormalGoods) )
            {
                //寻找满足条件的数据
                foreach ($saleNormalGoods as $item )
                {
                    $SpecList = array_column($item['SpecList'],'SpecificationPrice','SpecificationNum');
                    $Number   = $item['number'];
                    //满足库存和规格
                    if( !isset($orderDispatchGoods[$SkuId]) && isset($SpecList[$spec_num]) && ( ( !in_array($orderType,[9,10]) && $Number >= $buy_number && $is_router_user == 0) || in_array($orderType,[9,10]) || $is_router_user == 1) ) {
                        $orderDispatchGoods[$SkuId] = $item;
                    }
                }
            }
            
            //未找到满足条件的供应商商品
            if( empty($orderDispatchGoods[$SkuId]) ) {
                if( $param['isBbcOrder'] ) {
                    return Normal::returnCode(Normal::FAIL,"当前优选商品『{$goods_item['skucode']}』已下架,请重新选购商品！100002");
                }
                return Normal::returnCode(Normal::FAIL,"当前优选商品『{$goods_item['goods_name']},{$goods_item['skucode']}』已下架,请重新选购商品！100003");
            }
    
            unset($item);
        }
        
        
        $binfo = Business::instance()->getUserInfoByBid($param['bid']);
        //当前商户价格模板
        $tplInfo = @json_decode($binfo['template_price_tpl'],true);
        if( $binfo['is_router_user'] == 1 && empty($tplInfo) ) {
            $tplInfo = [
                'price_settlement_rate' => '0',
                'price_rate' => '0',
                'price_market_rate' => '0',
            ];
        }
        
        //组装当前用户下单的结算价规则
        $args = [
            'binfo'             => $binfo,
            'tplInfo'           => $tplInfo,
            'goodsList'         => $orderDispatchGoods,
        ];
        $res = $this->getUserAuthGoodsList($args);
        if( $res['code'] != Normal::SUC ) {
            return $res;
        }
        
        $userAuthGoodsList = $res['data'];
        
        return Normal::returnCode(Normal::SUC,"获取优选商品成功",[
            'userAuthGoodsList' => $userAuthGoodsList,
            'bestGoodsList'     => $orderDispatchGoods
        ]);
    }
    
    /**
     * 获取渠道授权数据的五层结构
     * 此方法为所有和商品查看相关的总入口分页方法
     * @param int $param['page'] 分页
     * @param int $param['limit'] 分页限制大小
     * @param string $param['pageLastID'] 上一页的最后一条数据ID
     * @param int $param['query_type'], 1 - 商品授权推送，2-查看渠道商品搜索分页，3-渠道商品导出
     * @param 其它参数代表搜索条件的入参
     */
    public function getUserPushGoodsList(array $param = [])
    {
        if( !isset($param['bid']) ) {
            return Normal::returnCode(Normal::FAIL,"bid参数必传");
        }
        
        if( empty($param['query_type']) ) {
            return Normal::returnCode(Normal::FAIL,"query_type参数必传，查询数据来源不能为空!");
        }
        
        if( !in_array($param['query_type'],['1','2','3']) ) {
            return Normal::returnCode(Normal::FAIL,"query_type参数必须在1,2,3 枚举以内!");
        }

        $bid = (int)$param['bid'];
        if( $param['bid'] > 0 ) {
            $binfo = Business::instance()->getUserInfoByBid($bid);
            if( empty($binfo) ) {
                return Normal::returnCode(Normal::FAIL,"渠道用户信息不存在");
            }
            $tplInfo = @json_decode($binfo['template_price_tpl'],true);
            if( $binfo['has_template'] != 1 || empty($tplInfo)) {
                return Normal::returnCode(Normal::FAIL,"渠道未统一设置价格体系规则，请先设置价格体系！");
            }
        }else{
            $binfo = [
                'id'                    => 0,
                'bid'                   => 0,
                'is_market_price_send'  => 0,
                'is_router_user'        => 0,
                'user_tax_mode'         => 0,
                'is_channel'            => 0
            ];
            $tplInfo = [
                'price_settlement_rate' => '0.00',
                'price_rate' => '0.00',
                'price_market_rate' => '0.00',
            ];
        }
        
        $result_query_where = $this->getWhereParameters($param);
        if( $result_query_where['code'] != Normal::SUC ) {
            return $result_query_where;
        }
        
        //获取真实数据
        $where      = $result_query_where['data'];
        $limit      = $param['limit'] ?? 15;
        $page       = $param['page'] ?? 0;
        $query_type = $param['query_type'];
        $is_channel = $binfo['is_channel'];

        //有分页ID授权的情况下，需要特殊查询处理数据，最后一页的数据ID
        if( $query_type == 1 ) 
        {
            if( !isset($param['pageLastID']) ) {
                return Normal::returnCode(Normal::FAIL,"query_type = 1的情况下，pageLastID 必须传，默认为空!");
            }
            
            $pageLastID = !empty($param['pageLastID']) ? $param['pageLastID'] : 0 ;
            
            //先查询出第一页所有满足条件的ID数据,按照正序进行处理
            $forPageData = CommodityChannelBestGoods::instance()->getTableByBuID($bid)->select(['ID']);
            
            if(!empty($param['skuids']) && is_array($param['skuids'])){
                $forPageData->whereIn('SkuID',$param['skuids']);
            } elseif (!empty($param['channelGIds']) && is_array($param['channelGIds'])){
                $forPageData->whereIn('ID',$param['channelGIds']);
            } else {
                $forPageData->where('ID','>',$pageLastID);
            }
            
            $forPageData = $forPageData
            ->where('BuID',$bid)
            ->where('CommodityStatus',1)
            ->where('SelfBuild',0)
            ->orderBy('ID','asc')
            ->limit($limit)
            ->get()
            ->toArray();
            
            //分页结束
            if( empty($forPageData) ) {
                return Normal::returnCode(Normal::SUC,"获取成功",[]);
            }
            
            $cc_ids = array_column($forPageData,'ID');
            
            //组装查询条件字段
            $_extra_where = [
                'limit'         => $limit,
                'pageLastID'    => $param['pageLastID'] ?? 0,
                'cc_ids'        => $cc_ids,
                'is_channel'    => $is_channel
            ];
            
            $dataList  = $this
            ->getGoodsQuery($bid,$where,$_extra_where)
            ->orderBy("a_cop.ID","asc")
            ->get()
            ->toArray();
        }
        else if( $query_type == 2 ) //渠道商品列表查询
        {
            $_extra_where = [
                'is_channel'    => $is_channel
            ];
            
            //授权分页
            $pageInfo = $this
            ->getGoodsQuery($bid,$where,$_extra_where)
            ->orderBy("a_cop.ID","asc")
            ->paginate($limit,$this->getGoodsColumns(), 'page', $page,true)
            ->toArray();
            
            if( empty($pageInfo['data']) ) {
                return Normal::returnCode(Normal::SUC,"获取成功",[
                    'total' => $pageInfo['total'],
                    'data'  => [],
                    'page'  => $page,
                    'limit' => $limit
                ]);
            }
            $dataList = $pageInfo['data'];
        }
        else if( $query_type == 3 ) //渠道商品导出
        {
            //导出查询分页
            $_extra_where = [
                'is_channel'    => $is_channel
            ];
            $offset = ($page - 1) * $limit;
            $dataList = $this
            ->getGoodsQuery($bid,$where,$_extra_where)
            ->orderBy("a_cop.ID","asc")
            ->offset($offset)
            ->limit($limit)
            ->get()
            ->toArray();
            
            //分页结束
            if( empty($dataList) ) {
                return Normal::returnCode(Normal::SUC,"获取成功",[]);
            }
        }
        
        //获取默认运费模板
        $defaultTemplateInfo = TemplateDelivery::instance()->getDefaultTemplateInfo();
        //获取毛利规则
        $goodsLevelConfig = SystemCacheConfigService::instance()->getCacheInfoBySystemCode('goods_level');
        
        //获取五层结构数据
        $list = $this->getChannelPushGoodsByPriceTpl($binfo,$tplInfo,$dataList,$goodsLevelConfig,$defaultTemplateInfo);
        
        //如果是后台授权的商品查看及导出情况下，无须太多字段
        $_result_data_list = [];
        if( in_array($query_type,['2','3']) )
        {
            foreach ($list as $goods)
            {
                $skuInfo = current($goods['skuList']);
                $store   = current($skuInfo['storeList']);
                $batch   = current($store['goods_batch_list']);
                $spec    = current($batch['spec_list']);
                $_result_data_list[] = [
                    'commodity_name'    	=> $goods['goodsName'],
                    'goods_id'          	=> $goods['goodsId'],
                    'skucode'           	=> $skuInfo['skucode'],
                    'barcode'           	=> $skuInfo['barcode'],
                    'commodity_type'    	=> $goods['goodsType'],
                    'goods_type_name'   	=> CommodityArchives::instance()->goods_types[$goods['goodsType']],
                    'fclass_name'       	=> $goods['categoryFPreName'],
                    'sclass_name'       	=> $goods['categoryPreName'],
                    'tclass_name'       	=> $goods['categoryName'],
                    'brand_name'        	=> $goods['brandName'],
                    'country_name'      	=> $goods['countryName'],
                    'spec_list'         	=> $batch['spec_list'],
                    'tax_rate'          	=> $skuInfo['tax_rate'],
                    'has_rate'          	=> $skuInfo['has_rate'] == 1 ? '是':'否',
                    'weight'            	=> $skuInfo['weight'],
                    'goods_level'       	=> $goods['goods_level'],
                    'batch_no'          	=> $batch['batch_no'],
                    'is_free_postfree'  	=> $goods['isFreePostFee'],
                    'company_name'      	=> base64_decode(base64_decode($store['sp_attr_name'])),
                    'number'            	=> $batch['num'],
                    'expired_date'      	=> $spec['expired_date'],
                    'conSumptionTaxRate'    => $goods['conSumptionTaxRate'],
                    'conSumptionPrice'      => $goods['conSumptionPrice'],
                    'isCombineSupplierCommodity' => $goods['isCombineSupplierCommodity'],
                ];
            }
            
            if( $query_type == 2 ) {
                $pageInfo['data'] = $_result_data_list;
                $list = $pageInfo;
            }
            else
            {
                $list = $_result_data_list;
            }
        }
        
        return Normal::returnCode(Normal::SUC,"获取成功",$list);
    }
    
    /**
     * 通过SpuIDs范围查询
     * 商品的规格属性值
     * @param array $SpuIds
     */
    public function getSpuSpecAttrListBySpuIds(array $SpuIds)
    {
        $table = CommoditySpecValue::instance()->getTable();
        $list = CommoditySpecValue::instance()
        ->from($table." AS csv")
        ->select([
            'csp.ID AS SpuID',
            'csa.AttrName',
            'csv.SpecID as AttrID',
            'csv.ValueName',
            'csg.GroupName',
            'cga.GroupID'
        ])
        ->join("commodity_spec_attr as csa","csv.SpecID","=","csa.ID")
        ->join("commodity_spu as csp","csp.ExtraID","=","csa.SpuExtraID")
        ->join("commodity_group_attr as cga","cga.AttrID","=","csa.ID")
        ->join("commodity_spec_group as csg","csg.ID","=","cga.GroupID")
        ->whereIn('csp.ID',$SpuIds)
        ->get()
        ->toArray();
        if( empty($list) ) {
            return [];
        }
        $spuSpecList = [];
        $specList = [];
        foreach ($list as $item) 
        {
            $specList[$item['SpuID']][$item['GroupID']][$item['AttrID']]['AttrName'] = $item['AttrName'];
            $specList[$item['SpuID']][$item['GroupID']][$item['AttrID']]['AttrSpecValueList'][]   = [
                'ValueName' => $item['ValueName']
            ];
            //$spuSpecList[$item['SpuID']][$item['GroupID']]['GroupID']   = $item['GroupID'];
            $spuSpecList[$item['SpuID']][$item['GroupID']]['GroupName'] = $item['GroupName'];
            $spuSpecList[$item['SpuID']][$item['GroupID']]['GroupList'] = $specList[$item['SpuID']][$item['GroupID']];
        }
        
        foreach ($spuSpecList as & $groupList) 
        {
            rsort($groupList);
            foreach ($groupList as & $attrList)
            {
                rsort($attrList['GroupList']);
            }
        }
        return $spuSpecList;
    }
    

    /**
     * 通过寻找对应upc 的规格属性
     * 商品的规格属性值
     * @param array $SpuIds
     */
    public function getSkuSpecAttrList(array $goodsList)
    {
        $specValueIds = [];
        $newSpecValueList = [];
        foreach ($goodsList as $skuInfo)
        {
            $tempList = $this->getExplodeAttrValueBody($skuInfo['SpecValue']);
            $newSpecValueList = array_merge($newSpecValueList,$tempList);
        }
        
        $ValueIds = array_column($newSpecValueList,'ValueId');
        $table = CommoditySpecValue::instance()->getTable();
        $list = CommoditySpecValue::instance()
        ->from($table." AS csv")
        ->select([
            'csa.AttrName',
            'csv.SpecID as AttrID',
            'csv.ValueName',
            'csv.ID as ValueID'
        ])
        ->join("commodity_spec_attr as csa","csv.SpecID","=","csa.ID")
        ->whereIn('csv.ID',$ValueIds)
        ->get()
        ->toArray();
        return array_column($list,null,'ValueID');
    }
    
    /**
     * 获取拆分组装规格属性值
     * @param string $SpecValue
     */
    public function getExplodeAttrValueBody(string $SpecValue)
    {
        if( empty($SpecValue) ) {
            return [];
        }
        
        $newSpecValueList = [];
        $SpecValue = \explode(";",$SpecValue);
        foreach ($SpecValue as $value)
        {
            $items = \explode(":",$value);
            $newSpecValueList[] = [
                'SpecID'    => $items[0],
                'ValueId'   => $items[1]
            ];
        }
        return $newSpecValueList;
    }
    
    /**
     * 获取当前UPC BARCODE 规格属性功能
     * @param array $attrMap
     * @param string $specValue
     * @return mixed
     */
    public function getBarCodeAttrValues(array $attrMap,string $specValue)
    {
        $specValueArr = $this->getExplodeAttrValueBody($specValue);        
        if( empty($specValueArr) ) {
            return [];
        }
        
        $specArr = [];
        foreach ($specValueArr as $value)
        {
            $ValueId = $value['ValueId'];
            if( !empty($attrMap[$ValueId]) ) {
                $specArr[] = [
                    'AttrName'      => $attrMap[$ValueId]['AttrName'],
                    'ValueName'     => $attrMap[$ValueId]['ValueName'],
                ];
            }
        }
        return $specArr;
    }
    
    /**
     * 获取推送数据的价格模板处理
     * @param array $binfo 商户信息
     * @param array $tplInfo  Template表指定价格模板id的数据
     * @param array $goodsList  商品sku信息
     * @param array $goodsLevelConfig 毛利编码配置
     * @param array $templateInfo 运费模板
     * @return mixed
     */
    public function getChannelPushGoodsByPriceTpl( array $binfo = [] ,array $tplInfo = [],array $goodsList = [],array $goodsLevelConfig = [],array $templateInfo = [] )
    {
        //是否给下游统一推送建议零售价
        //获取所有运费模板ID
        $FreightTemplateIDs = array_filter(array_unique(array_column($goodsList, 'FreightTemplateID')));
        $goodsFreightTemplateLists = TemplateDelivery::instance()->getMultiGoodsTemplateListsByTemplateIds($FreightTemplateIDs);
        $is_market_price_send = $binfo['is_market_price_send'];
        
        
        //查询是否有组合装商品情况
        $whereCombineGoods = [];
        foreach ($goodsList as $item) 
        {
            if( isset($item['IsCombineSupplierCommodity']) && $item['IsCombineSupplierCommodity'] == 1 && !empty($item['CombineGoodsContents']) ) 
            {
                $combineGoodsContents = @json_decode($item['CombineGoodsContents'],true);
                if( !empty($combineGoodsContents) ) 
                {
                    $GoodsSupplierSkuCodes = array_column($combineGoodsContents,'GoodsSupplierSkuCode');
                    $whereInstr = "'".\implode("','",$GoodsSupplierSkuCodes)."'";
                    $whereCombineGoods[] = "(SupplierCompanyID = '{$item['SupplierID']}' and SupplierCommoditySkuCode in ({$whereInstr}))";
                }
            }
        }
        
        //存在组合装条件的情况
        $combineGoodsRowsMap = [];
        if( !empty($whereCombineGoods) ) 
        {
             $whereCombineGoodsStr = implode(" or ",$whereCombineGoods);
             $combineGoodsRows = SupplierCommodity::instance()->select(['SupplierCompanyID','SupplierCommoditySkuCode','SKUCode'])->whereRaw($whereCombineGoodsStr)->get()->toArray();
             $combineGoodsRowsMap = [];
             foreach ($combineGoodsRows as $row) 
             {
                 $combineGoodsRowsMap[$row['SupplierCompanyID']][$row['SupplierCommoditySkuCode']] = $row['SKUCode'];
             }
        }
        
        //批量查询商品规格属性数据
        $attrValueMap = $this->getSkuSpecAttrList($goodsList);
        
        //批量查询SPU商品规格属性数据
        /* $spuIds = array_column($goodsList,'SpuID');
        $spuSpecList = $this->getSpuSpecAttrListBySpuIds($spuIds); */
        
        $dataList = [];
        foreach ($goodsList as $skuInfo )
        {
            //如果是需要选择路由的渠道
            if( $binfo['is_router_user'] == 1 ) {
                $skuInfo['SKUCode'] = $skuInfo['SupplierCommoditySkuCode'];
            }

        
            //处理规格问题
            $spec = $this->getGoodsSpecList($skuInfo,$tplInfo,$binfo);
            $specList = $spec['SpecList'];
            $specList = array_map(function( & $value ) use ($is_market_price_send){
                unset($value['priceCost']);
                if( $is_market_price_send == 1 ) {
                    $value['priceSettlement'] = $value['price'];
                }
                return $value;
            }, $specList);
            $firstSpec = current($specList);
            
            //处理图片
            $Images         = @json_decode(empty($skuInfo['Images']) ? '' : $skuInfo['Images'],true);
            $images         = $this->getResetImagesList($Images);
            //毛利编码
            $goodsLevel     = $this->getGoodsLevel((float)$skuInfo['MarketPrice'],(float)$spec['maxSinglePrice'],$goodsLevelConfig);
            //商品类型
            $commodity_type = $skuInfo['CommodityType'];
            $goodsTypeName  = CommodityArchives::instance()->goods_types[$commodity_type] ?? '未定义';
            ////商品分佣比例
            $CommissionLevelList=[];
            if(!empty($skuInfo['CommissionLevelList'])){
                $CommissionLevelList=@json_decode($skuInfo['CommissionLevelList'],true);
            }
            $commodity_customer_info = [
                'payDeclare'                    => $skuInfo['PayDeclare'],
                'payDeclareEnumDescription'     => $skuInfo['PayDeclare'] == 1 ? '需要支付单申报' : '',
                'orderDeclare'                  => $skuInfo['OrderDeclare'],
                'orderDeclareEnumDescription'   => $skuInfo['OrderDeclare'] == 1 ? '需要订单申报' : '',
                'enterpriseName'                => $skuInfo['EnterpriseName'],
                'enterpriseCode'                => $skuInfo['EnterpriseCode'],
                'declareSKUCode'                => $skuInfo['DeclareSKUCode'],
                'declareCommodityName'          => $skuInfo['DeclareCommodityName'],
                'function'                      => $skuInfo['DeclareSKUCode'],
                'declareBarCode'                => $skuInfo['DeclareBarCode'],
                'customMeasurementUnitCode'     => $skuInfo['DeclareMeasurementUnitCode'],
                'customMeasurementUnit'         => $skuInfo['DeclareMeasurementUnit'],
                'customCountryCode'             => $skuInfo['DeclareCountryCode'],
                'customCountry'                 => $skuInfo['DeclareCountry'],
                'customCurrencyCode'            => $skuInfo['DeclareCurrencyCode'],
                'customCurrency'                => $skuInfo['DeclareCurrency'],
                'customSpecifications'          => $skuInfo['DeclareSpecifications'],
            ];

            //处理spu属性贵格问题
            $spuSpecJsonBody = @json_decode($skuInfo['SpuSpecJsonBody'],true);

            //商品运费模板ID
            $FreightTemplateID = $skuInfo['FreightTemplateID'];
            $freight_template = isset($goodsFreightTemplateLists[$FreightTemplateID]) ? $goodsFreightTemplateLists[$FreightTemplateID] : $templateInfo;

            //处理规格库存问题
            $batch = [
                'supplier_skuid'    => $skuInfo['SupplierCommodityID'],
                'supplier_skucode'  => $skuInfo['SKUCode'],
                'defaultStock'      => $skuInfo['DefaultStock'],
                'num'               => $skuInfo['Number'],
                'batch_no'          => $skuInfo['BatchNo'],
                'batch_id'          => $skuInfo['BatchID'],
                'status'            => 1,
                'spec_list'         => $specList
            ];
            $goods_batch_list = [$batch];
            if(!empty($skuInfo['SupplierAlias'])){
                $SupplierAlias=$skuInfo['SupplierAlias'];
            }else{
                $SupplierAlias=$skuInfo['SupplierName'];
            }
            $store = [
                "linkSkuID"                     => $skuInfo['SkuID'],
                "store_customer_name"           => $SupplierAlias,
                "store_type"                    => $skuInfo['WarehouseType'],
                "storeDisplayName"              => $SupplierAlias,
                "store_id"                      => $skuInfo['WarehouseID'],
                "store_code"                    => $skuInfo['WarehouseCode'],
                "store_name"                    => $SupplierAlias,
                "store_account"                 => $skuInfo['WarehouseAccount'],
                "store_isCombineOrders"         => $skuInfo['IsCombineOrders'] == 1 ? true : false,
                "store_mode"                    => $skuInfo['OrderDockMode'],
                "supplier_skuid"                => $skuInfo['SupplierCommodityID'],
                "supplier_skucode"              => $skuInfo['SupplierCommoditySkuCode'],
                "plat_skucode"                  => $skuInfo['PlatSkuCode'],
                "supplier_id"                   => $skuInfo['SupplierID'],
                "sp_attr_name"                  => base64_encode(base64_encode($skuInfo['SupplierName'])),
                "supplier_alias"                => $SupplierAlias,
                "supplier_code"                 => $skuInfo['SupplierCode'],
                "supplier_companystatus"        => $skuInfo['CompanyStatus'],
                "supplier_commoditydockmode"    => $skuInfo['OrderDockMode'],
                "supplier_stockdockmode"        => 0,
                "supplier_orderdockmode"        => 0,
                'BusinessLicenseNo'             => $skuInfo['BusinessLicenseNo'],
                'FastDeliveryType'              => $skuInfo['FastDeliveryType'],
                "goods_batch_list"              => $goods_batch_list,
            ];
            $storeList = [$store];

            
            $has_rate = $skuInfo['IsHaveTaxRate'];
            if($binfo['user_tax_mode']==1){
                $has_rate = 1;
            }elseif ($binfo['user_tax_mode']==2){
                $has_rate = 0;
            }
    
            //组装商品名称，拼接规格字段
            $SupplierCommoditySpec = $skuInfo['SupplierCommoditySpec'] ?? '';
            $spec_value = '';
            $SupplierCommoditySpecJsonBody = @json_decode($SupplierCommoditySpec,true);
            if( $SupplierCommoditySpecJsonBody ) 
            {
                $specs = array_column($SupplierCommoditySpecJsonBody, 'spec_value');
                if( !empty($specs) ) 
                {
                    foreach($specs as $spec)
                    {
                        if( empty($spec) ) {
                            continue;
                        }
                        
                        if(strpos($skuInfo['Name'],(string)$spec) !== false)
                        {
                            continue;
                        }
                        else
                        {
                            $spec_value .= "【" .$spec."】";
                        }
                    }
                }
            }
            
            //处理组合装数据功能
            if( !empty($skuInfo['CombineGoodsContents']) ) 
            {
                $SupplierID = $skuInfo['SupplierID'];
                $combineGoodsContents = @json_decode($skuInfo['CombineGoodsContents'],true);
                foreach ($combineGoodsContents as & $cbg_goods) 
                {
                    //如果存在原始的子商品数据，则需要将子商品自编码加上
                    if( !empty($combineGoodsRowsMap[$SupplierID]) && !empty($combineGoodsRowsMap[$SupplierID][$cbg_goods['GoodsSupplierSkuCode']]) ) {
                        $cbg_goods['SkuCode'] = $combineGoodsRowsMap[$SupplierID][$cbg_goods['GoodsSupplierSkuCode']];
                    } else {
                        $cbg_goods['SkuCode'] = '';
                    }
                }
                
                $skuInfo['CombineGoodsContents'] = json_encode($combineGoodsContents,JSON_UNESCAPED_UNICODE);
            }
            
            //是否组合装标识
            $skuInfo['IsCombineSupplierCommodity'] = !empty($skuInfo['CombineGoodsContents']) ? 1 : 0;
            
            //获取upc bcarcode 细的规格属性
            $skuSpecValueArr = $this->getBarCodeAttrValues($attrValueMap,$skuInfo['SpecValue']);
            
            $dataList[] =[
                'goodsCopID'        => $skuInfo['cop_id'],
                'goodsId'           => $skuInfo['GoodsID'],
                'goodsName'         => $skuInfo['Name'] . $spec_value,
                'introName'         => $skuInfo['Name']."({$goodsTypeName})",
                'images'            => $images,
                'description'       => $skuInfo['Description'],
                'video'             => $skuInfo['Video'],
                'goodsType'         => $commodity_type,
                'goods_extends_type'=> $skuInfo['PriceControlType'] ?? 0,
                'goodsTypeName'     => $goodsTypeName,
                'status'            => 1,
                'categoryId'        => $skuInfo['ThirdClassificationID'],
                'categoryExtraId'   => (string)$skuInfo['ThirdClassExtraID'],
                'categoryCode'      => $skuInfo['ThirdClassificationCode'],
                'categoryName'      => $skuInfo['ThirdClassificationName'],
                'categoryPid'       => $skuInfo['SecondClassificationID'],
                'categoryPExtraId'  => (string)$skuInfo['SecondClassExtraID'],
                'categoryPreCode'   => $skuInfo['SecondClassificationCode'],
                'categoryPreName'   => $skuInfo['SecondClassificationName'],
                'categoryFPid'      => $skuInfo['FirstClassificationID'],
                'categoryFPExtraId' => (string)$skuInfo['FirstClassExtraID'],
                'categoryFPreCode'  => $skuInfo['FirstClassificationCode'],
                'categoryFPreName'  => $skuInfo['FirstClassificationName'],
                'categoryPreId'     => $skuInfo['FirstClassificationID'],
                'brandID'           => $skuInfo['BrandID'],
                'brandExtraId'      => $skuInfo['BrandExtraID'],
                'brandCode'         => $skuInfo['BrandCode'],
                'brandName'         => $skuInfo['BrandName'],
                'countryId'         => $skuInfo['CountryID'],
                'countryExtraId'    => $skuInfo['CountryExtraID'],
                'countryCode'       => $skuInfo['CountryCode'],
                'countryName'       => $skuInfo['CountryName'],
                'unitID'            => $skuInfo['UnitID'],
                'unitExtraId'       => $skuInfo['UnitExtraID'],
                'unitCode'          => $skuInfo['UnitCode'],
                'unitName'          => $skuInfo['UnitName'],
                'goods_level'       => $goodsLevel,
                'freight_template'  => $freight_template,
                'isFreePostFee'     => $skuInfo['IsFreeShipping'],
                'isNeedWeiXinPay'   => $skuInfo['IsNeedWeiXinPay'],
                'isNeedUploadIDCard'=> $skuInfo['IsNeedUploadIDCard'],
                'isNeedIDCard'      => $skuInfo['IsNeedIDCard'],
                'fastDeliveryType'  => $skuInfo['FastDeliveryType'],
                'conSumptionTaxRate' => $skuInfo['ConSumptionTaxRate'],
                'conSumptionPrice'   => $skuInfo['ConSumptionPrice'],
                'tags'              => !empty($skuInfo['Tags']) ? @json_decode($skuInfo['Tags'],true) : [],
                'goodsSpecAttr'     => $SupplierCommoditySpecJsonBody,
                'custom_id'         => $skuInfo['CustomID'] ?? 0,
                'isCombineSupplierCommodity' => $skuInfo['IsCombineSupplierCommodity'],
                'insuranceDay'      => $skuInfo['InsurancePriceDay'],
                'isInsurance'       => $skuInfo['IsInsurance'],
                'skuList'           => [
                    [
                        'skuId'                     => $skuInfo['SkuID'],
                        'skucode'                   => $skuInfo['SKUCode'],
                        'barcode'                   => $skuInfo['BarCode'],
                        'tax_rate'                  => $skuInfo['TaxRate'],
                        'tax_income_rate'           => $skuInfo['TaxInComeRate'],
                        'weight'                    => $skuInfo['Weight'],
                        'weightUnit'                => $skuInfo['WeightUnit'],
                        'limit_buy_count'           => $skuInfo['LimitBuyCount'],
                        'has_rate'                  => $has_rate,
                        'status'                    => 1,
                        'price'                     => $firstSpec['price'],
                        'price_cost'                => $firstSpec['priceSettlement'],
                        'price_market'              => $firstSpec['priceMarket'],
                        'price_control'             => $firstSpec['priceControl'],
                        'spu_name'                  => $skuInfo['SpuName'],
                        'spu_id'                    => $skuInfo['SpuID'],
                        'spu_extra_id'              => $skuInfo['SpuExtraID'],
                        'spu_status'                => $skuInfo['SpuStatus'],
                        'spu_default'               => $skuInfo['SpuDefault'],
                        'spu_spec_value'            => $skuInfo['SpecValue'],
                        'spu_spec_json_body'        => $skuInfo['SpuSpecJsonBody'],
                        'spu_spec_attr_list'        => !empty($skuSpecValueArr) ? json_encode($skuSpecValueArr,JSON_UNESCAPED_UNICODE) : '',
                        'commodity_customer_info'   => $commodity_customer_info,
                        'storeList'                 => $storeList,
                        "commission_level_list"     => $CommissionLevelList,
                    ]
                ],
            ];
        }
        return $dataList;
    }

     /**
     * 获取优选模板SQL
     * @param string $bid
     * @param array $where
     */
    public function getBestGoodsQuery( int $bid = 0 , $where = [] )
    {
        $table = CommodityOptimizationBbc::instance()->getTableByBuID($bid)->getTable();
        $minPriceSubQuery = CommodityOptimizationBbc::instance()->getTableByBuID($bid)
        ->from($table . " as a_cop")
        ->select([
            'a_cop.BuID',
            'a_cop.SkuID',
            Db::raw('min(a_cop.BatchSpecMinSinglePrice) AS BatchSpecMinSinglePrice')
        ])
        ->join("stockinfo_s2b as stock","stock.CommodityArchivesID","=","a_cop.SkuID")
        ->join("commodityarchives_s2b as com","com.ID","=","stock.CommodityArchivesID")
        ->join("suppliercommodity_s2b as scb","scb.ID","=","stock.SupplierCommodityID")
        ->whereRaw(implode(" AND ", $where))
        ->where('a_cop.CommodityStatus',1)
        ->groupBy("a_cop.SkuID");
        
        return CommodityOptimizationBbc::instance()->getTableByBuID($bid)
        ->from($table." AS b")
        ->select([
            Db::raw("max(b.ID) AS max_id")
        ])
        ->joinSub($minPriceSubQuery,"a",function($join){
            $join->on('a.SkuID', '=', 'b.SkuID')
            ->on('a.BuID','=','b.BuID')
            ->on('a.BatchSpecMinSinglePrice','=','b.BatchSpecMinSinglePrice');
        })
        ->groupBy("b.SkuID")
        ->where('b.CommodityStatus',1);
    }

    /**
     * 子查询
     * @param int $bid
     * @param array $subQuery
     * @param array $where
     */
    public function getLastQuery(int $bid,$subQuery = [], $where = [] )
    {
        $table = CommodityOptimizationBbc::instance()->getTableByBuID($bid)->getTable();
        
        //路由模式需要授权所有商品
        $queryMain = CommodityOptimizationBbc::instance()->getTableByBuID($bid)
        ->from($table." AS cop")
        ->select($this->getGoodsBaseColumns());
        
        //需要参加优选的商户
        $binfo = Business::instance()->getUserInfoByBid($bid);
        $isRouterUser = true;
        if ( ($bid > 0 && $binfo['is_router_user'] != 1) || $bid == 0 ) {
            $queryMain->joinSub($subQuery,"c",function($join){
                $join->on('c.max_id', '=', 'cop.ID');
            });
            
            $isRouterUser = false;
        }
        
        $join_mode = $isRouterUser ? 'leftJoin' : 'join';
        $queryMain->join("stockinfo_s2b as stock","stock.ID","=","cop.StockInfoID")
        ->join("commodityarchives_s2b as com","com.ID","=","stock.CommodityArchivesID")
        ->{$join_mode}("commoditydescription_s2b as cds",function($join){
            $join->on("cds.CommodityInfoID","=","com.ID")
            ->where("cds.IsDeleted",0);
        })->leftJoin("spuattr_s2b as spu","spu.ID","=","com.SpuID")
        ->join("suppliercommodity_s2b as scb","scb.ID","=","cop.SupplierSKUID")
        ->leftJoin("suppliercommoditycustominfo_s2b as scb_cus",function($join){
            $join->on("scb_cus.SupplierCommodityID","=","cop.SupplierSKUID")
            ->where("scb_cus.IsDeleted",0);
        })
        ->{$join_mode}("classifyinfo_s2b as f_class","f_class.ID","=","com.FirstClassificationID")
        ->{$join_mode}("classifyinfo_s2b as s_class","s_class.ID","=","com.SecondClassificationID")
        ->{$join_mode}("classifyinfo_s2b as t_class","t_class.ID","=","com.ThirdClassificationID")
        ->{$join_mode}("brandinfo_s2b as b","b.ID","=","com.BrandID")
        ->{$join_mode}("countryinfo_s2b as c","c.ID","=","com.CountryID")
        ->leftJoin("unitinfo_s2b as u","u.ID","=","com.UnitID")
        ->join("warehouseinfo_s2b as w","w.ID","=","stock.WarehouseID")
        ->join("suppliercompany_s2b as s","s.ID","=","stock.SupplierCompanyID");
        
        //如果是路由模式用户
        $whereRaw = implode(" AND ", $where);
        $whereRaw = str_replace("a_cop.","cop.", $whereRaw);
        $queryMain->whereRaw($whereRaw);
        
        return $queryMain->where('cop.BuID',$bid)
        ->orderBy("cop.ID","asc");
    }
    
    /**
     * 同步渠道商品数量
     * @param array $param['cid']           = 渠道ID
     * @param array $param['goods_count']   = 渠道同步商品数量
     * @param array $param['message_id']    = 消息ID
     */
    public function syncChannelGoodsCount( array $param = [])
    {
        if( empty($param['cid']) ) {
            return Normal::returnCode(Normal::FAIL,"except param cid");
        }
        
        if( !isset($param['goods_count']) ) {
            return Normal::returnCode(Normal::FAIL,"except param goods_count");
        }
        
        if( empty($param['message_id']) ) {
            return Normal::returnCode(Normal::FAIL,"except param message_id");
        }
        
        $binfo = Business::instance()->info(['channel_id'=>$param['cid']],['id']);
        if( empty($binfo) ) {
            return Normal::returnCode(Normal::FAIL,"云仓渠道信息不存在!");
        }
        
        $goods_count = $param['goods_count'];
        $where = [
            'id'    => $param['message_id'],
            'bid'   => $binfo['id'],
        ];
        
        MerchantGoodsSyncLog::instance()
        ->where($where)
        ->update([
            'sync_user_count'   => $goods_count,
            'sync_notify_time'  => time(),
        ]);
        
        return Normal::returnCode(Normal::SUC,"当前同步数量成功",[
            'sync_user_count'   => $goods_count
        ]);
    }
}